﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Reflection;
namespace Test
{
	/// <summary>
	/// <see cref="http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx"/>
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class ObjectReader<T> : IEnumerable<T>, System.Collections.IEnumerable where T : class, new()
	{
		Enumerator enumerator;		
		public ObjectReader(DbDataReader reader, Dictionary<FieldInfo,string> field_alias_map)
		{
			this.enumerator = new Enumerator(reader, field_alias_map);
		}
		public IEnumerator<T> GetEnumerator()
		{
			Enumerator e = this.enumerator;
			if (e == null)
			{
				throw new InvalidOperationException("Cannot enumerate more than once");
			}
			this.enumerator = null;			
			return e;
		}
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}
		class Enumerator : IEnumerator<T>, System.Collections.IEnumerator, IDisposable
		{
			DbDataReader reader;
			FieldInfo[] fields;
			int[] fieldLookup;
			T current;
			Dictionary<FieldInfo, string> field_alias_map;
			internal Enumerator(DbDataReader reader, Dictionary<FieldInfo, string> field_alias_map)
			{
				this.field_alias_map = field_alias_map;
				this.reader = reader;
				this.fields = typeof(T).GetFields();
				this.InitLookup();
			}
			public T Current
			{
				get { return this.current; }
			}
			object System.Collections.IEnumerator.Current
			{
				get { return this.current; }
			}
			public bool MoveNext()
			{
				if (this.reader.Read())
				{
					//if (this._fieldLookup == null)
					//{
						
					//}
					T instance = new T();
					for (int i = 0, n = this.fields.Length; i < n; i++)
					{
						int ordinal = this.fieldLookup[i];
						if (ordinal >= 0)
						{
							FieldInfo fi = this.fields[i];							
							if (this.reader.IsDBNull(ordinal))
							{
								fi.SetValue(instance, null);								
							}
							else
							{
								fi.SetValue(instance, this.reader.GetValue(ordinal));								
							}
						}
					}
					this.current = instance;
					return true;
				}
				return false;
			}
			public void Reset()
			{
			}
			public void Dispose()
			{
				this.reader.Dispose();
			}
			private void InitLookup()
			{
				Dictionary<string, int> reader_map //
					= new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
				int ordinal, n = this.reader.FieldCount, i = 0;

				for (ordinal = 0; ordinal < n; ordinal++)
				{
					reader_map.Add(this.reader.GetName(ordinal), ordinal);
				}
				this.fieldLookup = new int[this.fields.Length]; 				
				for (i = 0, n = this.fields.Length; i < n; i++)
				{
					int index;
					FieldInfo f = this.fields[i];
					string name = f.Name;//not used
					string alias = this.field_alias_map[f];
					if (reader_map.TryGetValue(alias, out index)) {
						this.fieldLookup[i] = index;
					}
					else
					{
						this.fieldLookup[i] = -1;
					}
				}
			}

		}
	}
 
 
}
